/*
Some helper functions.
*/

const PI = 3.1415926;

function l2distance( x1, y1, x2, y2 ) {
	var dx = x1 - x2;
	var dy = y1 - y2;
	return Math.sqrt(dx*dx + dy*dy);
}

// gets the distance between two latitude/longitude/altitude points.
function distance( lat0, lon0, alt0, lat1, lon1, alt1 ) {
	const pi = 3.1415926535;
	lat0 = lat0 * pi / 180.0;
	lat1 = lat1 * pi / 180.0;
	lon0 = lon0 * pi / 180.0;
	lon1 = lon1 * pi / 180.0;
	// radius of earth, in meters
	r = 6371000;
	dlat = lat1 - lat0;
	dlon = lon1 - lon0;
	
	a = Math.sin(dlat / 2) * Math.sin(dlat / 2) + Math.cos(lat0) * Math.cos(lat1) * Math.sin(dlon / 2) * Math.sin(dlon / 2);
	c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	horizontal = r*c;
	return Math.sqrt(horizontal * horizontal + (alt0 - alt1) * (alt0 - alt1));
}

// from http://www.electrictoolbox.com/pad-number-zeroes-javascript/
// pads the number to the given length.
// ex: pad(5,3) -> '005'
//     pad(123,3) -> '123'
function pad( number, length ) {
    var str = '' + number;
    while (str.length < length) {
        str = '0' + str;
    }
    return str;
}

function min( arr ) {
    var minVal = arr[0];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] < minVal){
            minVal = arr[i]
        }
    }
    return minVal;
}

function max( arr ) {
    var maxVal = arr[0];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] > maxVal){
            maxVal = arr[i]
        }
    }
    return maxVal;
}

function sum( arr ) {
    var sumVal = 0;
    for (var i = 0; i < arr.length; i++){
        sumVal = sumVal + arr[i]
    }
    return sumVal;
}

function mean( arr ) {
    return sum(arr) / arr.length;
}

function std( arr ) {
    var meanVal = mean(arr);
    var variance = 0;
    for (var i = 0; i < arr.length; i++){
        var diff =  (arr[i] - meanVal);
        variance = variance + diff * diff;
    }
    return Math.sqrt(variance / (arr.length-1));
}

function latitude_per_meter( lat, lon ) {
    return 0.00001 / distance(lat, lon, 0, lat+0.00001, lon, 0);
}

function longitude_per_meter( lat, lon ) {
    return 0.00001 / distance(lat, lon, 0, lat, lon+0.00001, 0);
}

function encodeHTML( s ) {
    return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
}

// creates an AJAX request.
function createRequest()
{
	if (window.XMLHttpRequest)
	{
		return new XMLHttpRequest();
	}
	if (window.ActiveXObject)  // for IE.
	{
		return new ActiveXObject("Microsoft.XMLHTTP");
	}
	return null;
}


function AssertException(message) { this.message = message; }
AssertException.prototype.toString = function () {
  return 'AssertException: ' + this.message;
}

function assert(exp, message) {
  if (!exp) {
    throw new AssertException(message);
  }
}

// randomly samples some percentage of objects from a distribution
// without replacement.
function randomSample( dist, percent ) {
	assert(percent >= 0, 'Percent must be nonnegative');
	assert(percent <= 1, 'Percent must be less than 1');
	
	var numToSample = Math.ceil(dist.length * percent);
	var sampleIndices = [];
	while (sampleIndices.length < numToSample){
		var proposed = Math.floor(Math.random() * dist.length);
		if (sampleIndices.indexOf(proposed) == -1){
			sampleIndices.push(proposed);
		}
	}
	sampleIndices = sampleIndices.sort();
	var sampled = [];
	for (var i in sampleIndices){
		sampled.push(dist[sampleIndices[i]]);
	}
	return sampled;
}

// implements the python range function, i.e., a list of integers
// from start (inclusive) to end (exclusive)
function range( start, end ) {
    var foo = [];
    for (var i = start; i <= end; i++)
        foo.push(i);
    return foo;
}